/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.jfonia.view.main;

import java.io.File;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jfonia.connect5.basics.BasicNode;
import org.jfonia.connect5.basics.Observer;
import org.jfonia.constants.ModelConstants;
import org.jfonia.language.LabelConstants;
import org.jfonia.language.LanguageResource;
import org.jfonia.model.ISequence;
import org.jfonia.model.elements.MusicalElement;
import org.jfonia.view.dialogs.OpenFileDialog;
import org.jfonia.view.dialogs.SaveFileDialog;
import org.jfonia.notation.Notation;
import org.jfonia.notation.StaffElementSequence;
import org.jfonia.view.elements.IElement;
import org.jfonia.view.labels.LabelPanel;
import org.jfonia.xml.Parser;
import percussiongenerator.model.Pair;

/**
 *
 * @author Rik Bauwens
 */
public class Project extends BasicNode implements Iterable<LeadSheet>
{
        private static Project instance;
        private BasicNode collectionObservable = new BasicNode();

        private Map<LeadSheet, String> leadSheets;
        private Map<Integer, LeadSheet> leadSheetIds;
        private LeadSheet currentLeadSheet;

        private Project()
        {
                super();
                leadSheets = new HashMap<LeadSheet, String>();
                leadSheetIds = new HashMap<Integer, LeadSheet>();
        }

        public Object getSelected()
        {
                if(Notation.getInstance().getSelectedElements() != null)
                {
                        Map<ISequence, List<MusicalElement>> sequences
                                = new HashMap<ISequence, List<MusicalElement>>();
                        for(IElement element : Notation.getInstance().getSelectedElements())
                        {
                                for(Pair<ISequence, MusicalElement> pair : element.getMusicalElements())
                                {
                                        if(!sequences.containsKey(pair.first))
                                                sequences.put(pair.first, new ArrayList<MusicalElement>());
                                        sequences.get(pair.first).add(pair.second);
                                }
                        }
                        return sequences;
                }
                //else
                //        return currentLeadSheet.getScribbles();
                return null;
        }

        public void add(Object object)
        {
                if(object instanceof Map && !((Map) object).isEmpty())
                {
                        int tickPosition = Notation.getInstance().getTickPosition();
                        Map<ISequence, List<MusicalElement>> sequences
                                = (Map<ISequence, List<MusicalElement>>) object;
                        for(ISequence sequence : sequences.keySet())
                        {
                                if(tickPosition < 0)
                                        sequence.addMusicalElements(sequences.get(sequence));
                                else
                                        sequence.insertMusicalElements(tickPosition, sequences.get(sequence));
                        }
                }
                //else
        }

        public void remove(Object object)
        {
                if(object instanceof List && !((List) object).isEmpty())
                {
                        if(((List) object).iterator().next() instanceof IElement)
                              ;
                        //else
                }
        }

        public void newLeadSheet()
        {
                setLeadSheet(null, new LeadSheet());
        }

        public void openLeadSheet()
        {
                String fileName = OpenFileDialog.getInstance().open();
                if(fileName == null || fileName.isEmpty())
                        return;

                for(LeadSheet leadSheet : leadSheets.keySet())
                {
                        if(leadSheets.get(leadSheet) != null &&
                                leadSheets.get(leadSheet).compareTo(fileName) == 0)
                        {
                                setLeadSheet(fileName, leadSheet);
                                notifyCollectionObservers();
                                return;
                        }
                }
                setLeadSheet(fileName, Parser.getInstance().readLeadSheet(fileName));
        }

        public void saveLeadSheet()
        {
                String fileName = leadSheets.get(currentLeadSheet);
                if(fileName == null || fileName.isEmpty())
                        saveLeadSheetAs();
                else
                        Parser.getInstance().writeLeadSheet(fileName, currentLeadSheet);
        }

        public void saveLeadSheetAs()
        {
                String fileName = SaveFileDialog.getInstance().open();
                if(fileName == null || fileName.isEmpty())
                        return;
                if(!fileName.endsWith(ModelConstants.EXTENSION))
                        fileName = fileName + "." + ModelConstants.EXTENSION;

                leadSheets.put(currentLeadSheet, fileName);
                Parser.getInstance().writeLeadSheet(fileName, currentLeadSheet);
                notifyCollectionObservers();
        }

        public static synchronized Project getInstance()
        {
                if(instance == null)
                        instance = new Project();
                return instance;
        }

        public LeadSheet getCurrentLeadSheet()
        {
                return currentLeadSheet;
        }

        public void setLeadSheet(String fileName, LeadSheet leadSheet)
        {
                new SetLeadSheet(fileName, leadSheet).start();
        }

        private class SetLeadSheet extends Thread
        {
                private String fileName;
                private LeadSheet leadSheet;

                public SetLeadSheet(String fileName, LeadSheet leadSheet)
                {
                        this.fileName = fileName;
                        this.leadSheet = leadSheet;
                }

                /**
                 * Procedure being called when the thread is started.
                 */
                @Override
                public void run()
                {
                        if(leadSheet == currentLeadSheet)
                                return;
                        MainFrame.getInstance().showLoader(fileName);
                        currentLeadSheet = leadSheet;
                        if(leadSheet != null && !leadSheets.containsKey(leadSheet))
                        {
                                leadSheets.put(leadSheet, fileName);
                                leadSheetIds.put(leadSheet.hashCode(), leadSheet);
                                notifyCollectionObservers();
                        }
                        //Leadsheet player
                        LeadSheetPlayer.getInstance().setLeadSheet(leadSheet);
                        //Notation toolbar
                        Notation.getInstance().setLeadSheet(leadSheet);
                        //Labels bar
                        if(leadSheet != null)
                                LabelPanel.getInstance().setLabels(leadSheet.getLabels());
                        else
                                LabelPanel.getInstance().setLabels(null);
                        notifyObservers();
                        //LoaderPanel.getInstance().setVisible(false);
                        MainFrame.getInstance().disposeLoader();
                }
        }

        public void showLeadSheet(int leadSheetId)
        {
                LeadSheet leadSheet = leadSheetIds.get(leadSheetId);
                setLeadSheet(leadSheets.get(leadSheet), leadSheet);
        }

        public boolean isOpened(int leadSheetId)
        {
                return leadSheetIds.get(leadSheetId) == currentLeadSheet;
        }

        public void closeLeadSheet(int leadSheetId)
        {
                if(leadSheetIds.containsKey(leadSheetId))
                        closeLeadSheet(leadSheetIds.get(leadSheetId));
        }

        public void closeLeadSheet(LeadSheet leadSheet)
        {
                if(leadSheets.containsKey(leadSheet))
                {
                        leadSheets.remove(leadSheet);
                        leadSheetIds.remove(leadSheet.hashCode());
                        if(leadSheet == currentLeadSheet)
                        {
                                if(leadSheets.isEmpty())
                                        setLeadSheet(null, null);
                                else
                                {
                                        LeadSheet nextLeadSheet = leadSheets.keySet().iterator().next();
                                        setLeadSheet(leadSheets.get(nextLeadSheet), nextLeadSheet);
                                }
                        }
                        notifyCollectionObservers();
                }
        }

        public String getHeader(LeadSheet leadSheet)
        {
                StringWriter stringWriter = new StringWriter();
                if(leadSheet.getName() != null && !leadSheet.getName().isEmpty())
                        stringWriter.append(leadSheet.getName());
                if(leadSheet.getAuthor() != null && !leadSheet.getAuthor().isEmpty())
                {
                        if(stringWriter.getBuffer().length() > 0)
                                stringWriter.append(" ");
                        stringWriter.append("(");
                        stringWriter.append(leadSheet.getAuthor());
                        stringWriter.append(")");
                }
                if(stringWriter.getBuffer().length() > 0)
                        return stringWriter.toString();

                if(leadSheets.containsKey(leadSheet) && leadSheets.get(leadSheet) != null
                        && !leadSheets.get(leadSheet).isEmpty())
                {
                        String fileName = leadSheets.get(leadSheet);
                        fileName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);
                        return fileName;
                }
                        

                return LanguageResource.getInstance().getLabel(LabelConstants.UNTITLED);
        }

        public int getLeadSheetCount()
        {
                return leadSheets.size();
        }
        
        private void notifyCollectionObservers()
        {
                collectionObservable.notifyObservers();
        }

        public void addCollectionObserver(Observer o)
        {
                collectionObservable.addObserver(o);
        }

        public void removeCollectionObserver(Observer o)
        {
                collectionObservable.removeObserver(o);
        }

        public Iterator<LeadSheet> iterator()
        {
                return leadSheets.keySet().iterator();
        }
}
